#include "Img.h"
#include <stdio.h>
#include <stdlib.h>
#include "Logger.h"
#include "string.h"
#include <unistd.h>
#include <math.h>
#include <string.h>
#include <assert.h>
#include "GameLib.h"

#include "SOIL/SOIL.h"

//OLD: lots of help from: http://users.ox.ac.uk/~orie1330/bmploader.html
//now using Simple Opengl Image Library [SOIL]

//#define pixel(pxarry,width,x,y,c)pxarry[(y*(width-1)+x)*4+c]

Img::Img(){
	texture=0;
	width=0;
	height=0;
}
Img::~Img(){
	//do nothing
}
void	Img::release(){
    if (texture==0){return;}
    glDeleteTextures(1,&texture);
}
bool	Img::load(const char *filename){
	if (texture != 0){release();}
	texture = SOIL_load_OGL_texture( filename, SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y|SOIL_FLAG_MIPMAPS );
	int trash;
	SOIL_load_image(filename, &width, &height, &trash, 4);
	if (glIsTexture(texture))
	{
		return true;
	}
	texture = 0;
	errLog.print(Str()<<"FAILED TO LOAD TEXTURE \""<<filename<<"\"");
	return false;
}
/*bool	Img::loadBmp(const char *filename){
	FILE* f;
    char header[54];
    unsigned int offset,filesize,rCh,gCh,bCh,aCh;
    unsigned int x,y,c,seperation,i,ii,sz,bpp;
    unsigned char *img,*data,colorTable[1024],byte;
    short colorShort;
    bool hasSeperation;
    
    data=0;
    i=0;
    ii=0;
    
    f=fopen(filename,"rb");		//open for reading in binary mode
	if(!f){errLog.print("Could not open the file");return false;}
    
	fread(header,54,1,f);//Bitmap header + DIB header
	if(header[0]!='B' || header[1]!='M')
	{
        errLog.print("Not a proper bitmap");
		fclose(f);
		return false;
	}
    filesize=*(unsigned int*)(header+2);
	offset=*(unsigned int*)(header+10);
	width=*(unsigned int*)(header+18);
	height=*(unsigned int*)(header+22);
    
    rCh=*(unsigned int*)(header+0x36);
    gCh=*(unsigned int*)(header+0x3A);
    bCh=*(unsigned int*)(header+0x3E);
    aCh=*(unsigned int*)(header+0x42);
    
    img=(unsigned char *)malloc(width*height*4*sizeof(unsigned char));
    memset(img,0,width*height*4*sizeof(unsigned char));
    
    
	bpp=int(header[28]);//colordepth
	switch(bpp)
	{
        case 32:
            fseek(f, offset, SEEK_SET);
            seperation=4-(width*4%4);
            sz=height*((width*4)+seperation);
            hasSeperation = filesize==sz+offset?true:false;
            data=(unsigned char*)malloc(sz*sizeof(unsigned char*));
            fread(data,sz,1,f);
            for (y=0;y<height;y++)
            {
                for (x=0;x<width;x++)
                {
                    img[ii  ]=data[i+1];//R
                    img[ii+1]=data[i+2];//G
                    img[ii+2]=data[i+3];//B
                    img[ii+3]=data[i  ];//A
                    i+=4;
                    ii+=4;
                }
                if (hasSeperation){i+=seperation;}
            }
            break;
        case 24:
            fseek(f,offset,SEEK_SET);
            seperation=4-(width*3%4);
            sz=height*((width*3)+seperation);
            hasSeperation = filesize==sz+offset?true:false;
            data=(unsigned char*)malloc(sz*sizeof(unsigned char));
            fread(data,sz,1,f);
            for(y=0;y<height;y++)
            {
                for(x=0;x<width;x++)
                {
                    img[ii  ]=data[i+2];//R
                    img[ii+1]=data[i+1];//G
                    img[ii+2]=data[i  ];//B
                    img[ii+3]=0xFF;     //A
                    i+=3;
                    ii+=4;
                }
                if (hasSeperation){i+=seperation;}
            }
            break;
        
        case 16:
            //mainLog.print(Str()<<"r:"<<(int)rCh<<" g:"<<(int)gCh<<" b:"<<(int)bCh<<" a:"<<(int)aCh);
            fseek(f, offset, SEEK_SET);
            seperation=4-(width*2%4);
            sz=height*((width*2)+seperation);
            hasSeperation = filesize==sz+offset?true:false;
            data=(unsigned char*)malloc(sz*sizeof(unsigned char));
            fread(data, sz, 1, f);
            for (y=0;y<height;y++)
            {
                for (x=0;x<width;x++)
                {
                    //RRRRRGGGGGGBBBBB
                    //data[i]^=data[i+1]^=data[i]^=data[i+1];
                    colorShort=*(short*)(data+i);
                    
                    aCh=0x0000;
                    rCh=0x7C00;
                    gCh=0x03E0;
                    bCh=0x001F;
                    
                    img[ii  ]=0xFF;//(colorShort&rCh)*255/rCh;//(unsigned char)((float)(data[i+1]&0x0F)*0xFF/0x0F);//R
                    img[ii+1]=0x00;//(colorShort&gCh)*255/gCh;//(unsigned char)((float)(data[i+1]&0xF0)*0xFF/0xF0);//G
                    img[ii+2]=0x00;//(colorShort&bCh)*255/bCh;//(unsigned char)((float)(data[i  ]&0x0F)*0xFF/0x0F);//B
                    if (aCh==0){img[ii+3]=0xFF;}else{img[ii+3]=(colorShort&aCh)*255/aCh;}//A
                    i+=2;
                    ii+=4;
                }
                if (seperation){i+=seperation;}
            }
            
        case 8:
            fread(colorTable,256*4,1,f);
            fseek(f,offset,SEEK_SET);
            for(y=0;y<height;y++)
            {
                for(x=0;x<width;x++)
                {		
                    fread(&byte,1,1,f);			
                    for(c=0;c<3;c++)
                    {
                        pixel(img,width,x,y,c)=colorTable[byte*4+2-c];//color table lookup
                    }
                }
            }
            break;
            
        default:
            fclose(f);
            errLog.print(Str()<<"An unknown bitdepth of: "<<(int)bpp);
            return false;
	}
    
	if(ferror(f))
	{
        errLog.print("Image file had an error while reading");
		fclose(f);
		return false;
	}
	
	fclose(f);
    
	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);
	gluBuild2DMipmaps(GL_TEXTURE_2D, 3, (float)width, (float)height, GL_RGBA, GL_UNSIGNED_BYTE, img);
    
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    
	if (data){free(data);}
    if (img){free(img);}
    
	return true;
}
bool	Img::loadJpg(const char *filename){
    release();
    return loadBmp(filename);
}
bool	Img::loadPng(const char *filename){
    release();
    return loadBmp(filename);
}
bool	Img::loadTiff(const char *filename){
    release();
    FILE *f;
    f=fopen(filename, "rb");
    if (!f){errLog.print("Could not open the file");return false;}
    
    return true;
}
bool	Img::loadTGA(const char * filename){
    return false;
}
bool	Img::loadUncompressedTGA(const char * filename){
    char header[12];
    GLubyte *data;
    FILE *f;
    unsigned int bpp,bytespp,size,i;
    GLint type;
    f = fopen(filename, "rb");
	if (!f)
	{										
		errLog.print("Could not open the file");
	}
    fread(header, 12, 1, f);
	width  = (header[1] * 256) + header[0];// Determine the TGA width	 (highbyte*256+lowbyte)
	height = (header[3] * 256) + header[2];// Determine the TGA height (highbyte*256+lowbyte)
	bpp	= header[4];
	if(width <= 0 || height <= 0 || (bpp != 24 && bpp !=32))
	{
		fclose(f);
        errLog.print("Could not understand the tga");
		return false;
	}
	if(bpp == 24)
	{
		type = GL_RGB;
	}
	else
	{
		type = GL_RGBA;
	}
	bytespp	= (bpp / 8);
	size = (bytespp * width * height);
	data = (GLubyte *)malloc(size*sizeof(GLubyte));
	if(data == 0)
	{
        fclose(f);
		errLog.print("Ran out of memory for malloc");
        return false;
	}
	if (fread(data, 1, size, f) != size)	// Attempt to read image data
	{
		fclose(f);
        if (data!=0){free(data);}
		return false;
	}
	for (i = 0; i < size; i+=bytespp)
	{
        data[i]^=data[i+2]^=data[i]^=data[i+2];
	}
	fclose(f);
    
    glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);
	gluBuild2DMipmaps(GL_TEXTURE_2D, 3, (float)width, (float)height, type, GL_UNSIGNED_BYTE, data);
    
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    
	return true;
}
bool	Img::loadCompressedTGA(const char * filename){ 
    char header[12];
    FILE *f;
    unsigned int bpp,bytespp,size;
    GLubyte *data;
    
    f=fopen(filename, "rb");
    if (!f)
    {
        errLog.print("could not open the file");
        return false;
    }
	if(fread(header, 12, 1, f) == 0)
	{
		errLog.print("Could not read info header");
		fclose(f);
		return false;
	}
	width  = (header[1] * 256) + header[0];
	height = (header[3] * 256) + header[2];
	bpp	= header[4];
	if(width <= 0 || height <= 0 || (bpp != 24 && bpp !=32))
	{
		errLog.print("Unknown tga format");
        fclose(f);
		return false;
	}
	bytespp	= bpp / 8;
	size = bytespp * width * height;
	data = (GLubyte*)malloc(size);
	if(data == 0)
	{
		errLog.print("Ran out of memory for malloc");
		fclose(f);
		return false;
	}
	GLuint pixelcount	= height * width;
	GLuint currentpixel	= 0;
	GLuint currentbyte	= 0;
	GLubyte * colorbuffer = (GLubyte *)malloc(bytespp);
	do
	{
		GLubyte chunkheader = 0;
		if (fread(&chunkheader, sizeof(GLubyte), 1, f) == 0)
		{
			errLog.print("Could not read RLE header");
			if (f != 0){fclose(f);}
			if (data != 0){free(data);}
			return false;
		}
		if(chunkheader < 128)
		{
			chunkheader++;
			for(short counter = 0; counter < chunkheader; counter++)
			{
				if (fread(colorbuffer, 1, bytespp, f) != bytespp)
				{
					errLog.print("Could not read image data");
					if(f != 0){fclose(f);}
					if(colorbuffer != 0){free(colorbuffer);}
					if(data != 0){free(data);}
					return false;
				}
				data[currentbyte  ] = colorbuffer[2];
				data[currentbyte+1] = colorbuffer[1];
				data[currentbyte+2] = colorbuffer[0];
				if(bytespp == 4)//32 bit img
				{
					data[currentbyte+3] = colorbuffer[3];
				}
				currentbyte+=bytespp;
				currentpixel++;
				if(currentpixel > pixelcount)
				{
					errLog.print("Too many pixels read");
					if(f != 0){fclose(f);}	
					if(colorbuffer != 0){free(colorbuffer);}
					if(data != 0){free(data);}
					return false;
				}
			}
		}
		else
		{
			chunkheader -= 127;
			if(fread(colorbuffer, 1, bytespp, f) != bytespp)
			{	
				errLog.print("Could not read from file");
                if(f != 0){fclose(f);}	
                if(colorbuffer != 0){free(colorbuffer);}
                if(data != 0){free(data);}
                return false;
			}
			for(short counter = 0; counter < chunkheader; counter++)
			{
				data[currentbyte  ] = colorbuffer[2];
				data[currentbyte+1] = colorbuffer[1];
				data[currentbyte+2] = colorbuffer[0];
				if (bytespp == 4)//32 bit img
				{
					data[currentbyte + 3] = colorbuffer[3];
				}
				currentbyte+=bytespp;
				currentpixel++;
				if(currentpixel > pixelcount)
				{
					errLog.print("Too many pixels read");
					if(f != 0){fclose(f);}	
                    if(colorbuffer != 0){free(colorbuffer);}
                    if(data != 0){free(data);}
                    return false;
				}
			}
		}
	}while(currentpixel < pixelcount);
	fclose(f);
	return true;
}*/
int		Img::getWidth(){
	return width;
}
int		Img::getHeight(){
	return height;
}
void	Img::use(){
    if (!glIsTexture(texture)){return;}
	gameLib.setColor(C_WHITE);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	glBindTexture(GL_TEXTURE_2D,texture);
    //mainLog.print(Str()<<"Bounded the texture GL id #"<<(int)texture);
}
